package com.yami.shop.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yami.shop.bean.dto.ShopWithdrawCashConfigDto;
import com.yami.shop.bean.enums.SendType;
import com.yami.shop.bean.enums.ShopWalletChangeReason;
import com.yami.shop.bean.enums.ShopWithdrawCashStatus;
import com.yami.shop.bean.enums.WithdrawCashEnum;
import com.yami.shop.bean.model.ShopBankCard;
import com.yami.shop.bean.model.ShopDetail;
import com.yami.shop.bean.model.ShopWallet;
import com.yami.shop.bean.model.ShopWithdrawCash;
import com.yami.shop.bean.param.hnaPay.BaseTransForm;
import com.yami.shop.bean.pay.WithdrawalDto;
import com.yami.shop.common.bean.SysConfig;
import com.yami.shop.common.config.Constant;
import com.yami.shop.common.exception.YamiShopBindException;
import com.yami.shop.common.i18n.I18nMessage;
import com.yami.shop.common.i18n.LanguageEnum;
import com.yami.shop.common.util.*;
import com.yami.shop.config.HnaPayConfig;
import com.yami.shop.config.ShopConfig;
import com.yami.shop.dao.ShopWithdrawCashMapper;
import com.yami.shop.service.*;
import lombok.AllArgsConstructor;
import org.apache.poi.ss.usermodel.Sheet;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.util.*;

/**
 * 商家提现申请信息
 *
 * @author
 * @date 2019-09-19 14:22:08
 */
@Service
@AllArgsConstructor
public class ShopWithdrawCashServiceImpl extends ServiceImpl<ShopWithdrawCashMapper, ShopWithdrawCash> implements ShopWithdrawCashService {

    private final ShopWithdrawCashMapper shopWithdrawCashMapper;

    private final ShopWalletLogService shopWalletLogService;

    private final ShopWalletService shopWalletService;

    private final ShopBankCardService shopBankCardService;


    private final ShopDetailService shopDetailService;

    private final SmsLogService smsLogService;

    private final SysConfigService sysConfigService;

    private final ShopConfig shopConfig;

    private final HnaPayConfig hnaPayConfig;

    private final String SHOP_WITHDRAW_CASH_PREFIX = "ShopWithdrawCash_";

    /**
     * 导出excel列（中文）
     */
    private final static String[] EXPORT_COL_CN = {
            "店铺名称", "提现金额", "创建时间",  "更新时间", "审核时间", "审核状态"
    };
    /**
     * 导出excel列（英文）
     */
    private final static String[] EXPORT_COL_EN = {
            "Shop Name", "Withdrawal amount", "Creation time", "Update time", "Review time", "Audit status"
    };


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void withdrawCash(ShopWithdrawCash shopWithdrawCash) {
        Long shopId = shopWithdrawCash.getShopId();
        ShopWallet dbShopWallet = shopWalletService.getShopWalletByShopId(shopId);

        if (shopWithdrawCash.getAmount() > dbShopWallet.getSettledAmount()) {
            // 可提现金额不足
            throw new YamiShopBindException("yami.shop.cash.no.enough");
        }

        checkConfig(shopWithdrawCash);
        ShopDetail shopDetail = shopDetailService.getById(shopId);
        if (!smsLogService.checkValidCode(shopDetail.getReceiveMobile(), shopWithdrawCash.getCode(), SendType.VALID)){
            // 验证码有误或已过期
            throw new YamiShopBindException("yami.user.code.error");
        }
        // 插入提现申请记录
        shopWithdrawCashMapper.insert(shopWithdrawCash);

        ShopWallet newShopWallet = BeanUtil.map(dbShopWallet, ShopWallet.class);
        newShopWallet.setSettledAmount(Arith.sub(dbShopWallet.getSettledAmount(), shopWithdrawCash.getAmount()));
        newShopWallet.setFreezeAmount(Arith.add(dbShopWallet.getFreezeAmount(), shopWithdrawCash.getAmount()));

        // 乐观锁更新钱包信息
        if (!shopWalletService.updateById(newShopWallet)) {
            // 失败则抛异常
            // 服务器繁忙
            throw new YamiShopBindException("yami.network.busy");
        }
        // 提现记录
        shopWalletLogService.saveShopWalletLog(dbShopWallet, newShopWallet, ShopWalletChangeReason.APPLY_CASH, null);
    }

    private void checkConfig(ShopWithdrawCash shopWithdrawCash) {
        ShopWithdrawCashConfigDto config = getConfig();
        if(shopWithdrawCash.getAmount()<config.getWithdrawCashLeast()){
            throw new YamiShopBindException("提现金额过少，最低提现:"+config.getWithdrawCashLeast());
        }
        if(Objects.nonNull(config.getWithdrawCashMax())&&shopWithdrawCash.getAmount()>config.getWithdrawCashMax()){
            throw new YamiShopBindException("提现金额过大,最高提现:"+config.getWithdrawCashMax());
        }
        Integer frequency = config.getFrequency();
        Date startTimes;
        Date endTimes;
        String date;
        if(frequency.equals(WithdrawCashEnum.WEEKEND.value())){
            startTimes = DateUtils.getCurrentWeekStartTimes(new Date());
            endTimes = DateUtils.getCurrentWeekEndTimes(new Date());
            date = WithdrawCashEnum.WEEKEND.date();
        } else if (frequency.equals(WithdrawCashEnum.MONTH.value())) {
            startTimes = DateUtils.getMonthFirstOrLastDay(new Date(), 0);
            endTimes = DateUtils.getMonthFirstOrLastDay(new Date(), 1);
            date = WithdrawCashEnum.MONTH.date();
        } else {
            startTimes = DateUtils.getCurrentYearStartTime(new Date());
            endTimes = DateUtils.getCurrentYearEndTime(new Date());
            date = WithdrawCashEnum.YEAR.date();
        }
        long count = checkCount(startTimes, endTimes, shopWithdrawCash);
        if(count+1>config.getNumber()){
            throw new YamiShopBindException("超出提现频率限制,当前提现频率为:"+date+" "+config.getNumber()+"次");
        }
        shopWithdrawCash.setFee(ObjectUtil.isNull(config.getFree())? 1.0 : config.getFree().doubleValue());
    }

    private long checkCount(Date startTime,Date endTime,ShopWithdrawCash shopWithdrawCash){
        return shopWithdrawCashMapper.selectCount(Wrappers.lambdaQuery(ShopWithdrawCash.class)
                .eq(ShopWithdrawCash::getShopId,shopWithdrawCash.getShopId())
                .ge(ShopWithdrawCash::getCreateTime, startTime)
                .le(ShopWithdrawCash::getCreateTime, endTime));
    }

    @Override
    public IPage<ShopWithdrawCash> pageShopWithdrawCash(PageParam<ShopWithdrawCash> page, ShopWithdrawCash shopWithdrawCash) {
        return shopWithdrawCashMapper.pageShopWithdrawCash(page, shopWithdrawCash);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void auditWithdrawCash(Long withdrawCashId, ShopWithdrawCash shopWithdrawCash, Long userId, String openId) {
        ShopWithdrawCash dbShopWithdrawCash = shopWithdrawCashMapper.selectById(withdrawCashId);
        if (!dbShopWithdrawCash.getStatus().equals(ShopWithdrawCashStatus.WAITAUDIT.value())) {
            // 该申请已被审核
            throw new YamiShopBindException("yami.examine.already");
        }
        Date now = new Date();
        dbShopWithdrawCash.setAuditingTime(now);
        dbShopWithdrawCash.setAuditorId(userId);
        dbShopWithdrawCash.setStatus(shopWithdrawCash.getStatus());
        dbShopWithdrawCash.setRemarks(shopWithdrawCash.getRemarks());
        dbShopWithdrawCash.setPayingTime(shopWithdrawCash.getPayingTime());
        dbShopWithdrawCash.setPayingCardNo(shopWithdrawCash.getPayingCardNo());
        dbShopWithdrawCash.setPayingAccount(shopWithdrawCash.getPayingAccount());

        ShopWallet dbShopWallet = shopWalletService.getShopWalletByShopId(dbShopWithdrawCash.getShopId());
        ShopWallet newShopWallet = BeanUtil.map(dbShopWallet, ShopWallet.class);
        // 审核通过
        if (Objects.equals(shopWithdrawCash.getStatus(), 1)) {
            ShopBankCard shopBankCard = shopBankCardService.getById(shopWithdrawCash.getShopBankCardId());
            if(ObjectUtil.isNull(shopBankCard)){
                throw new YamiShopBindException("未找到店铺提现银行卡信息");
            }
            WithdrawalDto withdrawalDto = new WithdrawalDto();
            withdrawalDto.setWithdrawCashId(shopWithdrawCash.getCashId().toString());
            withdrawalDto.setBusinessType("08");
            withdrawalDto.setBindCardAgrNo(shopBankCard.getBindCardAgrNo());
            withdrawalDto.setCardNo(shopBankCard.getCardNo());
            withdrawalDto.setTranAmount(shopWithdrawCash.getAmount().toString());
            withdrawalDto.setXsUserId(shopWithdrawCash.getShopXsUserId());
            withdrawalDto.setServiceAmount(shopWithdrawCash.getFee().toString());
            String domainName = shopConfig.getDomain().getApiDomainName();
            withdrawalDto.setWithdrawalNoticeUrl(domainName + "/notice/pay/shopWithdrawCash/" + shopWithdrawCash.getCashId().toString());
            BaseTransForm base = hnaPayConfig.getT002TransForm(withdrawalDto);
            try {
                Map<String, Object> post = hnaPayConfig.post(base);
                if (!post.get("resultCode").toString().equals("0000")) {
                    throw new YamiShopBindException("提现调用新生失败！");
                }
            } catch (Exception e) {
                throw new YamiShopBindException("提现异常{}",e);
            }
            //减去冻结的金额
            newShopWallet.setFreezeAmount(Arith.sub(dbShopWallet.getFreezeAmount(), dbShopWithdrawCash.getAmount()));
            shopWalletLogService.saveShopWalletLog(dbShopWallet, newShopWallet, ShopWalletChangeReason.PASS_CASH, null);

        } else if (Objects.equals(shopWithdrawCash.getStatus(), -1)) {
            // 将冻结的金额返回到已结算金额
            newShopWallet.setSettledAmount(Arith.add(dbShopWallet.getSettledAmount(), dbShopWithdrawCash.getAmount()));
            newShopWallet.setFreezeAmount(Arith.sub(dbShopWallet.getFreezeAmount(), dbShopWithdrawCash.getAmount()));
            shopWalletLogService.saveShopWalletLog(dbShopWallet, newShopWallet, ShopWalletChangeReason.REFUSE_CASH, null);
        } else {
            // 审核状态有误
            throw new YamiShopBindException("yami.examine.status.error");
        }

        // 乐观锁更新钱包信息
        if (!shopWalletService.updateById(newShopWallet)) {
            // 失败则抛异常
            // 服务器繁忙
            throw new YamiShopBindException("yami.network.busy");
        }
        shopWithdrawCashMapper.updateById(dbShopWithdrawCash);
    }

    @Override
    public ShopWithdrawCash getShopWithdrawCashByShopId(Long shopId) {
        return shopWithdrawCashMapper.getShopWithdrawCashByShopId(shopId);
    }

    @Override
    public void exportShopWithdrawCash(ShopWithdrawCash shopWithdrawCash, HttpServletResponse response) {
        List<ShopWithdrawCash> shopWithdrawCashList = shopWithdrawCashMapper.listExportByParam(shopWithdrawCash);
        if (CollUtil.isEmpty(shopWithdrawCashList)) {
            return;
        }
        ExcelWriter writer = ExcelUtil.getBigWriter();
        final List<String> exportCol = Arrays.asList(Objects.equals(I18nMessage.getLang(), LanguageEnum.LANGUAGE_ZH_CN.getLang()) ? EXPORT_COL_CN : EXPORT_COL_EN);
        // 最大列数量
        int maxColNum = exportCol.size();

        Sheet sheet = writer.getSheet();
        //设置列宽
        this.setColumnWidth(sheet);
        // 标题
        writer.merge(maxColNum - 1, Objects.equals(I18nMessage.getLang(), LanguageEnum.LANGUAGE_ZH_CN.getLang()) ? "商家提现信息整理" : "Merchant withdrawal information collation");
        // 列名
        writer.writeRow(exportCol);
        int rowIndex = 2;
        for(ShopWithdrawCash withdrawCash : shopWithdrawCashList){
            int col = -1;
            // 店铺名称
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++col, col, withdrawCash.getShopName());
            // 提现金额
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++col, col, withdrawCash.getAmount());
            // 创建时间
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++col, col, withdrawCash.getCreateTime());
            // 更新时间
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++col, col, withdrawCash.getUpdateTime());
            // 审核时间
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++col, col, withdrawCash.getAuditingTime());
            // 审核状态
            ShopWithdrawCashStatus shopWithdrawCashStatus = ShopWithdrawCashStatus.instance(withdrawCash.getStatus());
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++col, col, Objects.isNull(shopWithdrawCashStatus) ? "" : shopWithdrawCashStatus.text());

            ++rowIndex;
        }
        PoiExcelUtil.writeExcel(response, writer);
    }

    @Override
    public void saveConfig(ShopWithdrawCashConfigDto shopWithdrawCashDto) {
        CheckParam(shopWithdrawCashDto);
        SysConfig sysConfig = new SysConfig();
        sysConfig.setRemark("提现配置");
        sysConfig.setParamValue(Json.toJsonString(shopWithdrawCashDto));
        sysConfig.setParamKey(Constant.WITHDRAW_CONFIG);
        sysConfigService.saveOrUpdateSysConfigByKey(sysConfig);
    }

    private void CheckParam(ShopWithdrawCashConfigDto shopWithdrawCashDto) {
        Double least = shopWithdrawCashDto.getWithdrawCashLeast();
        Double max = shopWithdrawCashDto.getWithdrawCashMax();
        Integer frequency = shopWithdrawCashDto.getFrequency();
        Integer number = shopWithdrawCashDto.getNumber();
        if(Objects.isNull(least)||least<1){
            throw new YamiShopBindException("最低金额设置错误");
        }
        if(Objects.nonNull(max)&&max<least){
            throw new YamiShopBindException("最大金额设置错误");
        }
        if(Objects.isNull(frequency)||(!frequency.equals(WithdrawCashEnum.WEEKEND.value())&&!frequency.equals(WithdrawCashEnum.MONTH.value())&&!frequency.equals(WithdrawCashEnum.YEAR.value()))){
            throw new YamiShopBindException("提现频率设置错误");
        }
        if(Objects.isNull(number)||number<1){
            throw new YamiShopBindException("提现次数设置错误");
        }
    }

    @Override
    public ShopWithdrawCashConfigDto getConfig() {
        ShopWithdrawCashConfigDto shopWithdrawCashConfigDto = sysConfigService.getSysConfigObject(Constant.WITHDRAW_CONFIG,ShopWithdrawCashConfigDto.class);
        if(Objects.isNull(shopWithdrawCashConfigDto)){
            shopWithdrawCashConfigDto = new ShopWithdrawCashConfigDto();
            shopWithdrawCashConfigDto.setWithdrawCashLeast(100D);
            shopWithdrawCashConfigDto.setFrequency(1);
            shopWithdrawCashConfigDto.setNumber(1);
            shopWithdrawCashConfigDto.setFree(1);
        }
        return shopWithdrawCashConfigDto;
    }
    private void setColumnWidth(Sheet sheet) {
        // 店铺名称
        sheet.setColumnWidth(0, 20 * 256);
        // 提现金额
        sheet.setColumnWidth(1, 20 * 256);
        // 创建时间
        sheet.setColumnWidth(2, 20 * 256);
        // 更新时间
        sheet.setColumnWidth(3, 20 * 256);
        // 审核时间
        sheet.setColumnWidth(4, 20 * 256);
        // 审核状态
        sheet.setColumnWidth(5, 20 * 256);

    }
}
